package top.hmtools.wxmp.webpage.authorize;

import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson.JSON;

import top.hmtools.wxmp.core.httpclient.HmHttpClientFactoryHandle;
import top.hmtools.wxmp.core.model.ErrcodeBean;

/**
 * 微信OAuth2工具
 * @author Hybomyth
 *
 */
public class OAuth2Tools {
	
	private static final Logger logger = LoggerFactory.getLogger(OAuth2Tools.class);
	
	private static final String OAuth2_URI = "https://open.weixin.qq.com/connect/oauth2/authorize";

	/**
	 * 获取要进行微信oauth2授权的跳转URL（微信服务器）
	 * <br>第一步：用户同意授权，获取code
	 * @param appid	公众号的唯一标识，从微信申请的appid
	 * @param redirect_uri	授权后重定向的回调链接地址， 请使用 urlEncode 对链接进行处理，要进行跳转的我方服务器URL
	 * @param scope	应用授权作用域，snsapi_base （不弹出授权页面，直接跳转，只能获取用户openid），snsapi_userinfo （弹出授权页面，可通过openid拿到昵称、性别、所在地。并且， 即使在未关注的情况下，只要用户授权，也能获取其信息 ）
	 * @param state	重定向后会带上state参数，开发者可以填写a-zA-Z0-9的参数值，最多128字节，原样返回参数数据
	 * @return
	 * @throws UnsupportedEncodingException
	 */
	public static String getOAuth2UrlStr(String appid,String redirect_uri,EScope scope,String state){
		if(StringUtils.isAnyBlank(appid,redirect_uri)||scope==null){
			return null;
		}
		
		StringBuffer result = new StringBuffer(OAuth2_URI+"?");
		result.append("appid="+appid);
//		result.append("&redirect_uri="+URLEncoder.encode(redirect_uri, "UTF-8"));
		result.append("&redirect_uri="+redirect_uri);
		result.append("&response_type=code");
		result.append("&scope="+scope.toString());
		if(StringUtils.isNotBlank(state)){
			result.append("&state="+state);
		}
		result.append("#wechat_redirect");
		return result.toString();
	}
	
	/**
	 * oauth微信授权流程中获取的access_token
	 * <br>第二步：通过code换取网页授权access_token
	 * @param code
	 * @param appid
	 * @param appSecret
	 * @return
	 */
	public static OAuth2ATBean getAccessToken(String code,String appid,String appSecret){
		OAuth2ATBean result =null;
		CloseableHttpClient httpClient = HmHttpClientFactoryHandle.getPoolingHttpClient();
		HttpGet get = new HttpGet("https://api.weixin.qq.com/sns/oauth2/access_token?appid="+appid+"&secret="+appSecret+"&code="+code+"&grant_type=authorization_code");
		try {
			if(logger.isDebugEnabled()){
				logger.debug("oauth微信授权流程中获取的access_token 请求URL：{}",get.getURI().toString());
			}
			CloseableHttpResponse response = httpClient.execute(get);
			InputStream content = response.getEntity().getContent();
			String contentStr = IOUtils.toString(content, "utf-8");
			if(logger.isDebugEnabled()){
				logger.debug("oauth微信授权流程中获取的access_token原文：{}",contentStr);
			}
			result = JSON.parseObject(contentStr, OAuth2ATBean.class);
		} catch (IOException e) {
			logger.error("oauth微信授权流程中获取的access_token发生异常：",e);
		}
		return result;
	}
	
	/**
	 * 第三步：刷新access_token（如果需要）
	 * @param appid
	 * @param refreshToken
	 * @return
	 */
	public static OAuth2ATBean refreshToken(String appid,String refreshToken){
		OAuth2ATBean result =null;
		CloseableHttpClient httpClient = HmHttpClientFactoryHandle.getPoolingHttpClient();
		HttpGet get = new HttpGet("https://api.weixin.qq.com/sns/oauth2/refresh_token?appid="+appid+"&grant_type=refresh_token&refresh_token="+refreshToken);
		try {
			if(logger.isDebugEnabled()){
				logger.debug("oauth微信授权流程中刷新access_token 请求URL：{}",get.getURI().toString());
			}
			CloseableHttpResponse response = httpClient.execute(get);
			InputStream content = response.getEntity().getContent();
			String contentStr = IOUtils.toString(content, "utf-8");
			if(logger.isDebugEnabled()){
				logger.debug("oauth微信授权流程中获取的access_token原文：{}",contentStr);
			}
			result = JSON.parseObject(contentStr, OAuth2ATBean.class);
		} catch (IOException e) {
			logger.error("oauth微信授权流程中刷新access_token发生异常：",e);
		}
		return result;
	}
	
	/**
	 * oauth微信授权流程中获取用户信息
	 * <br>第四步：拉取用户信息(需scope为 snsapi_userinfo)
	 * @param auth2atBean
	 * @return
	 */
	public static OAuth2UserBean getUserInfo(OAuth2ATBean auth2atBean){
		OAuth2UserBean result = null;
		//https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN
		CloseableHttpClient httpClient = HmHttpClientFactoryHandle.getPoolingHttpClient();
		HttpGet get = new HttpGet("https://api.weixin.qq.com/sns/userinfo?access_token="+auth2atBean.getAccess_token()+"&openid="+auth2atBean.getOpenid()+"&lang=zh_CN");
		try {
			if(logger.isDebugEnabled()){
				logger.debug("oauth微信授权流程中获取用户信息 请求URL：{}",get.getURI().toString());
			}
			CloseableHttpResponse response = httpClient.execute(get);
			InputStream content = response.getEntity().getContent();
			String contentStr = IOUtils.toString(content, "utf-8");
			if(logger.isDebugEnabled()){
				logger.debug("oauth微信授权流程中获取用户信息的原文：{}",contentStr);
			}
			result = JSON.parseObject(contentStr, OAuth2UserBean.class);
		} catch (IOException e) {
			logger.error("oauth微信授权流程中获取用户信息发生异常：",e);
		}
		return result;
	}
	
	/**
	 * 附：检验授权凭证（access_token）是否有效
	 * @param accessToken		网页授权接口调用凭证,注意：此access_token与基础支持的access_token不同
	 * @param openId		用户的唯一标识
	 * @return
	 */
	public static ErrcodeBean checkAccessToken(String accessToken,String openId){
		ErrcodeBean result = null;
		CloseableHttpClient httpClient = HmHttpClientFactoryHandle.getPoolingHttpClient();
		HttpGet get = new HttpGet("https://api.weixin.qq.com/sns/auth?access_token="+accessToken+"&openid="+openId);
		try {
			if(logger.isDebugEnabled()){
				logger.debug("检验授权凭证（access_token）是否有效 请求URL：{}",get.getURI().toString());
			}
			CloseableHttpResponse response = httpClient.execute(get);
			InputStream content = response.getEntity().getContent();
			String contentStr = IOUtils.toString(content, "utf-8");
			if(logger.isDebugEnabled()){
				logger.debug("检验授权凭证（access_token）是否有效的原文：{}",contentStr);
			}
			result = JSON.parseObject(contentStr, ErrcodeBean.class);
		} catch (IOException e) {
			logger.error("检验授权凭证（access_token）是否有效发生异常：",e);
		}
		return result;
	}
	
	/**
	 * 授权作用域枚举
	 * @author Hybomyth
	 *
	 */
	public enum EScope{
		/**
		 * 不弹出授权页面，直接跳转，只能获取用户openid
		 */
		SNSAPI_BASE("snsapi_base"),
		/**
		 * 弹出授权页面，可通过openid拿到昵称、性别、所在地。并且， 即使在未关注的情况下，只要用户授权，也能获取其信息
		 */
		SNSAPI_USERINFO("snsapi_userinfo");
		
		private String scope;
		
		EScope(String scope){
			this.scope=scope;
		}
		
		@Override
		public String toString() {
			return this.scope;
		}
	}
}
